﻿using BackEnd;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace FrontEnd
{
	public partial class ObjectExplorer : UserControl
	{
		#region EVENTS
		public event EventHandler<Request> Request;
		public event EventHandler Refreshing;
		public event EventHandler<string> Attaching;
		public event EventHandler<string> Detaching;
		public event EventHandler<string> Connecting;
		public event EventHandler Disconnecting;
		#endregion

		#region CONSTRUCTOR(S)
		public ObjectExplorer()
		{
			InitializeComponent();
		}
		#endregion

		#region PUBLIC METHODS
		public void Populate(List<Database> databases)
		{
			tvObjects.Items.Clear();

			// add dbs
			foreach (var db in databases)
			{
				var dbNode = new Node(db.Name, NodeType.Database);
				if (db.Type == DatabaseType.Attached)
					dbNode = new Node(db.Name, NodeType.AttachedDatabase);

				// add tables
				if (db.Schema.Tables.Count > 0)
				{
					var tablesNode = new Node(string.Format("Tables ({0})", db.Schema.Tables.Count), NodeType.TableCollection);
					foreach (var tbl in db.Schema.Tables)
					{
						var tableNode = new Node(tbl.Name, NodeType.Table, tbl);

						// add column nodes
						var colsNode = new Node(string.Format("Columns ({0})", tbl.Columns.Count), NodeType.ColumnCollection);
						foreach (var col in tbl.Columns)
						{
							var colNode = new Node(col.Name, NodeType.Column, col);
							colsNode.Nodes.Add(colNode);
						}
						tableNode.Nodes.Add(colsNode);

						// add indexes
						if (tbl.Indexes.Count > 0)
						{
							var indexesNode = new Node(string.Format("Indexes ({0})", tbl.Indexes.Count), NodeType.IndexCollection);
							foreach (var idx in tbl.Indexes)
							{
								var idxNode = new Node(idx.Name, NodeType.Index, idx);
								indexesNode.Nodes.Add(idxNode);
							}
							tableNode.Nodes.Add(indexesNode);
						}

						// add trigger nodes
						if (tbl.Triggers.Count > 0)
						{
							var triggersNode = new Node(string.Format("Triggers ({0})", tbl.Triggers.Count), NodeType.TriggerCollection);
							foreach (var trigger in tbl.Triggers)
							{
								var triggerNode = new Node(trigger.Name, NodeType.Trigger, trigger);
								triggersNode.Nodes.Add(triggerNode);
							}
							tableNode.Nodes.Add(triggersNode);
						}

						tablesNode.Nodes.Add(tableNode);
					}

					dbNode.Nodes.Add(tablesNode);
				}

				// add views
				if (db.Schema.Views.Count > 0)
				{
					var viewsNode = new Node(string.Format("Views ({0})", db.Schema.Views.Count), NodeType.ViewCollection);
					foreach (var vw in db.Schema.Views)
					{
						var vwNode = new Node(vw.Name, NodeType.View, vw);

						// add column nodes
						var colsNode = new Node(string.Format("Columns ({0})", vw.Columns.Count), NodeType.ColumnCollection);
						foreach (var col in vw.Columns)
						{
							var colNode = new Node(col.Name, NodeType.Column, col);
							colsNode.Nodes.Add(colNode);
						}
						vwNode.Nodes.Add(colsNode);

						// add indexes
						if (vw.Indexes.Count > 0)
						{
							var indexesNode = new Node(string.Format("Indexes ({0})", vw.Indexes.Count), NodeType.IndexCollection);
							foreach (var idx in vw.Indexes)
							{
								var idxNode = new Node(idx.Name, NodeType.Index, idx);
								indexesNode.Nodes.Add(idxNode);
							}
							vwNode.Nodes.Add(indexesNode);
						}

						// add trigger nodes
						if (vw.Triggers.Count > 0)
						{
							var triggersNode = new Node(string.Format("Triggers ({0})", vw.Triggers.Count), NodeType.TriggerCollection);
							foreach (var trigger in vw.Triggers)
							{
								var triggerNode = new Node(trigger.Name, NodeType.Trigger, trigger);
								triggersNode.Nodes.Add(triggerNode);
							}
							vwNode.Nodes.Add(triggersNode);
						}

						viewsNode.Nodes.Add(vwNode);
					}
					dbNode.Nodes.Add(viewsNode);
				}

				tvObjects.Items.Add(dbNode);
			}
		}
		#endregion

		#region PRIVATE METHODS

		private void Refresh(object sender, RoutedEventArgs e)
		{
			OnRefreshing();
		}

		private void Connect(object sender, ExecutedRoutedEventArgs e)
		{
			OnConnecting(SelectFile());
		}

		private void Attach(object sender, ExecutedRoutedEventArgs e)
		{
			OnAttaching(SelectFile());
		}

		private void Detach(object sender, ExecutedRoutedEventArgs e)
		{
			var dbName = (string)e.Parameter;
			OnDetaching(dbName);
		}

		private string SelectFile()
		{
			var dlg = new OpenFileDialog();
			dlg.Filter = "SQLite Database (*.db3;*.sqlite)|*.db3;*.sqlite";
			var result = dlg.ShowDialog();
			return result == true ? dlg.FileName : null;
		}

		private void SelectTop(object sender, ExecutedRoutedEventArgs e)
		{
			var tableViewName = (string)e.Parameter;
			SelectTop(tableViewName);
		}

		private void SelectTop(string tableViewName)
		{
			var sqlReq = new DataRequest(this, "SELECT * FROM [" + tableViewName + "] LIMIT 1000;");
			OnRequest(sqlReq);
		}

		private void DoubleClick(object sender, MouseButtonEventArgs e)
		{
			var tv = (TreeView)sender;
			var n = (Node)tv.SelectedItem;
			if (n != null && n.Type == NodeType.Table || n.Type == NodeType.View)
			{
				SelectTop(n.Title);
				e.Handled = true;
			}
		}

		private void ShowCreateSQL(object sender, ExecutedRoutedEventArgs e)
		{
			var n = (Node)e.Parameter;
			var scriptReq = new ScriptRequest(this, n.ReferencedObject, ScriptType.Create);
			OnRequest(scriptReq);
		}

		private void CheckIntegrity(object sender, ExecutedRoutedEventArgs e)
		{
			var taskReq = new TaskRequest(this, null, TaskType.CheckIntegrity);
			OnRequest(taskReq);
		}

		private void Vacuum(object sender, ExecutedRoutedEventArgs e)
		{
			var taskReq = new TaskRequest(this, null, TaskType.Vacuum);
			OnRequest(taskReq);
		}

		protected virtual void OnRefreshing()
		{
			if (Refreshing != null)
				Refreshing(this, EventArgs.Empty);
		}

		protected virtual void OnRequest(Request request)
		{
			if (Request != null)
				Request(this, request);
		}

		protected virtual void OnAttaching(string filePath)
		{
			if (Attaching != null)
				Attaching(this, filePath);
		}

		protected virtual void OnDetaching(string dbName)
		{
			if (Detaching != null)
				Detaching(this, dbName);
		}

		protected virtual void OnConnecting(string filePath)
		{
			if (Connecting != null)
				Connecting(this, filePath);
		}

		#endregion
	}
}
